/*
 *	This file is part of Lounge-Cast.
 *
 *  Author:  	Markus Moormann
 *	E-mail:  	mmoorman@uni-osnabrueck.de
 *	Copyright:	University of Osnabrueck (Zentrum Virtuos)
 * 	Website:	www.uni-osnabrueck.de
 *	Date:		16.02.2011
 *
 *	Lounge-Cast is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *	Lounge-Cast is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Lounge-Cast.  If not, see <http://www.gnu.org/licenses/>.
 */

package multimedia.view.components.extended
{
	import com.adobe.utils.ArrayUtil;
	
	import flash.events.MouseEvent;
	
	import multimedia.control.event.media.SetApprovedStatusEvent;
	import multimedia.model.LoginModel;
	import multimedia.model.ModelLocator;
	import multimedia.model.ViewLocator;
	import multimedia.model.vo.MediaVO;
	import multimedia.view.components.MediaDetail;
	import multimedia.view.renderer.MediaTreeItemRenderer;
	
	import mx.binding.utils.BindingUtils;
	import mx.collections.ArrayCollection;
	import mx.controls.Tree;
	import mx.controls.listClasses.IListItemRenderer;
	import mx.core.ClassFactory;
	import mx.core.DragSource;
	import mx.core.mx_internal;
	import mx.events.DragEvent;
	import mx.events.TreeEvent;
	import mx.formatters.DateFormatter;
	import mx.managers.DragManager;
	import mx.managers.PopUpManager;

	/**
	 *
	 * @author Markus
	 */
	public class MediaTree extends Tree
	{

		private var viewlocator:ViewLocator = ViewLocator.getInstance();
		private var model:ModelLocator = ModelLocator.getInstance();
		private var openBranches:ArrayCollection = new ArrayCollection();

		/**
		 * default constructor
		 * all neccassary EventListener are added for drag&drop
		 * and clicking
		 */
		public function MediaTree()
		{
			super();
			//set the ItemRenderer
			itemRenderer = new ClassFactory(MediaTreeItemRenderer);
			//add all neccassary event listener
			addEventListener(DragEvent.DRAG_ENTER, onDragEnter);
			addEventListener(DragEvent.DRAG_OVER, onDragOver);
			addEventListener(DragEvent.DRAG_DROP, onDragDrop);
			addEventListener(DragEvent.DRAG_EXIT, onDragExit);
			addEventListener(DragEvent.DRAG_COMPLETE, onDragComplete);
			addEventListener(MouseEvent.CLICK, onClick);
			addEventListener(MouseEvent.DOUBLE_CLICK, showMediaPreview);

			showDataTips = true;
			doubleClickEnabled = true;
			dragEnabled = true;
			dropEnabled = true;
			dragMoveEnabled = false;
			wordWrap = true;
			variableRowHeight = true;

			dataTipFunction = dataTip;
			labelFunction = labelingFunction;
			
			BindingUtils.bindSetter(openAllOpenBranches, this, "dataProvider");
			this.addEventListener(TreeEvent.ITEM_OPEN, onItemOpen);
			this.addEventListener(TreeEvent.ITEM_CLOSE, onItemClose);
		}
		
		private function onItemOpen(event:TreeEvent):void
		{
			var index:Number = openBranches.getItemIndex(event.item.label);
			if(index == -1 && this.isItemOpen(event.item))
			{
				openBranches.addItem(event.item.label);
			}
		}
		
		private function onItemClose(event:TreeEvent):void
		{
			var index:Number = openBranches.getItemIndex(event.item.label);
			if(index != -1 && !this.isItemOpen(event.item))
			{
				openBranches.removeItemAt(index);
			}
		}
		
		/**
		 * open all branches in a tree
		 */
		private function openAllOpenBranches(data:ArrayCollection):void
		{
			for each(var branch:String in openBranches)
			{
				openBranch(this.dataProvider as ArrayCollection, branch);
			}
		}
		
		private function openBranch(list:ArrayCollection, branch:String):void
		{
			for each(var item:Object in list)
			{
				if(this.dataDescriptor.isBranch(item) && item.label == branch)
				{
					this.expandItem(item, true, true);
				}
				if(!(item is MediaVO) && item.children)
				{
					openBranch(item.children, branch);
				}
			}
		}

		/**
		 * labeling function for getting a label for branches and items
		 * 
		 * @return String cotaining the title of the media or, if the item is a branch, 
		 * the name of the branch + number of children
		 */
		private function labelingFunction(item:Object):String
		{
			if (item is MediaVO)
			{
				return item.title;
			}
			else if(item.label == "Text")
			{
				var amount:Number = 0;
				for each(var item2:Object in item.children)
				{
					if(!(item2 is MediaVO))
					{
						amount += item2.children.length;
					} else
					{
						amount ++;
					}
				}
				return item.label + "(" + amount + ")";
			} else
			{
				return item.label + "(" + item.children.length + ")";
			}
		}

		/**
		 * function for returning a tooltip text
		 * if the item is a media, some values such as title and author are
		 * shown, otherwise the name of the branch is used for tooltip
		 * 
		 * @return String containg the tooltip
		 */
		private function dataTip(item:Object):String
		{
			if (item is MediaVO)
			{
				var formatter:DateFormatter = new DateFormatter();
				formatter.formatString = "DD.MM.YYYY";
				var tooltip:String;
				var media:MediaVO = item as MediaVO;
				tooltip = "Titel: " + media.title + "\nDauer: " + media.duration + " Sekunden\n" +
						  "hinzugefügt von: " + media.addedBy.user_name +
						  "\nNewsticker ist " + (media.newsticker ? "" : "nicht") + " erlaubt\n" +
						  (!media.approved && media.addedBy.user_id == LoginModel.getInstance().user.user_id 
							  ? "Das Medium kann nur durch einen anderen Redakteur freigegeben werden" : "") +
						  (media.start_date > new Date().time / 1000 ? 
							  "Dieses Medium wird erst ab dem " + formatter.format(new Date(media.start_date * 1000)) + " angezeigt" : "");
				return tooltip;
			}
			else
			{
				return item.label + "(" + item.children.length + ")"
			}
		}

		/**
		 * called when drag entered the tree
		 */
		private function onDragEnter(event:DragEvent):void
		{
			var target:Tree = event.target as Tree;
			event.preventDefault();
			if (event.target != event.dragInitiator)
			{
				DragManager.acceptDragDrop(target);
			}
			target.showDropFeedback(event);
		}

		/**
		 * called when dragging around in the tree
		 */
		private function onDragOver(event:DragEvent):void
		{
			var target:Tree = event.target as Tree;
			event.preventDefault();
			target.showDropFeedback(event);
		}

		/**
		 * called when releasing the mouse button
		 */
		private function onDragDrop(event:DragEvent):void
		{
			var target:Tree = event.target as Tree;
			event.preventDefault();
			target.hideDropFeedback(event);
			(event.dragInitiator as Tree).hideDropFeedback(event);
			var ds:DragSource = event.dragSource;
			var arr:Array;
			if (ds.hasFormat("treeItems"))
			{
				arr = ds.dataForFormat("treeItems") as Array;
			}
			new SetApprovedStatusEvent(arr[0].media_id, target.id == "approvedMedia").dispatch();
			event.target.mx_internal::lastDragEvent = null; 
			event.target.mx_internal::resetDragScrolling(); 
		}

		/**
		 * called when exiting the tree
		 */
		private function onDragExit(event:DragEvent):void
		{
			(event.target as Tree).hideDropFeedback(event);
			(event.dragInitiator as Tree).hideDropFeedback(event);
		}

		/**
		 * called when the drag is complete
		 */
		private function onDragComplete(event:DragEvent):void
		{
			(event.target as Tree).hideDropFeedback(event);
			(event.dragInitiator as Tree).hideDropFeedback(event);
		}

		/**
		 * called when clicking an item
		 * if clicked item is a branch toggle it
		 */
		private function onClick(event:MouseEvent):void
		{
			var target:Tree = event.currentTarget as Tree;
			var item:Object = target.selectedItem;
			if (target.dataDescriptor.isBranch(item))
			{
				target.expandItem(item, !target.isItemOpen(item), true);
				var index:Number = openBranches.getItemIndex(item.label);
				if(index == -1 && this.isItemOpen(item))
				{
					openBranches.addItem(item.label);
				} else if(index != -1 && !this.isItemOpen(item))
				{
					openBranches.removeItemAt(index);
				}
				target.selectedItem = null;
				return;
			}
			if (target.id == "nonApprovedMedia")
			{
				/* if (item != null && (item as MediaVO).addedBy.user_id == LoginModel.getInstance().user.user_id)
				   {
				   selectedItem = null;
				 } */
			}
		}

		/**
		 * shows the details of the media
		 */
		private function showMediaPreview(event:MouseEvent):void
		{
			if (selectedItem)
			{
				var preview:MediaDetail = new MediaDetail();
				preview.media = selectedItem as MediaVO;
				PopUpManager.addPopUp(preview, this, true);
			}
		}

		/**
		 * function for helping user feedback und non selectable item
		 */
		override protected function mouseEventToItemRenderer(event:MouseEvent):IListItemRenderer
		{
			var listItem:IListItemRenderer = super.mouseEventToItemRenderer(event);
			if (listItem)
			{
				if (listItem.data)
				{
					if (disabledFunction(listItem.data))
					{
						return null;
					}
				}
			}
			return listItem;
		}

		/**
		 * 
		 * @return true if the media is from same user as logged in one, else false
		 */
		private function disabledFunction(item:Object):Boolean
		{
			if (item is MediaVO && (item as MediaVO).addedBy.user_id == LoginModel.getInstance().user.user_id &&
				!(item as MediaVO).approved)
			{
				return true;
			}
			return false;
		}


		private var selectionUpward:Boolean;

		/**
		 * function to prevent greyed items by keyboard
		 */
		override protected function finishKeySelection():void
		{
			super.finishKeySelection();

			var i:int;
			var uid:String;
			var rowCount:int = listItems.length;
			var partialRow:int = (rowInfo[rowCount - 1].y + rowInfo[rowCount - 1].height > listContent.height) ? 1 : 0;

			var listItem:IListItemRenderer;
			listItem = listItems[caretIndex - verticalScrollPosition][0];
			if (listItem)
			{
				if (listItem.data)
				{
					if (disabledFunction(listItem.data))
					{
						// find another visible item that is enabled
						// assumes there is one that is fully visible
						rowCount = rowCount - partialRow;
						var idx:int = caretIndex - verticalScrollPosition;
						if (selectionUpward)
						{
							// look up;
							for (i = idx - 1; i >= 0; i--)
							{
								listItem = listItems[i][0];
								if (!disabledFunction(listItem.data))
								{
									selectedIndex = i - verticalScrollPosition;
									return;
								}
							}
							for (i = idx + 1; i < rowCount; i++)
							{
								listItem = listItems[i][0];
								if (!disabledFunction(listItem.data))
								{
									selectedIndex = i - verticalScrollPosition;
									return;
								}
							}
						}
						else
						{
							// look down;
							for (i = idx + 1; i < rowCount; i++)
							{
								listItem = listItems[i][0];
								if (listItem && !disabledFunction(listItem.data))
								{
									selectedIndex = i - verticalScrollPosition;
									return;
								}
							}
							for (i = idx - 1; i >= 0; i--)
							{
								listItem = listItems[i][0];
								if (listItem && !disabledFunction(listItem.data))
								{
									selectedIndex = i - verticalScrollPosition;
									return;
								}
							}
						}
					}
				}
			}
		}

	}
}